

//void*

int DEFAULTSIZE;
int MINSIZE;

//typedef struct struct bloc_s bloc_t;
struct bloc_s {
	int size;
	int free;
	struct bloc_s *next;
	struct bloc_s *prev;
};

void *mem_start;
void *mem_end;
void *negVoid;


struct bloc_s *find_last(){
	struct bloc_s *b;

	if(mem_start == mem_end){
		return 0;
	}
	
    b = mem_start;
	
	while(b->next != 0){
		b = b->next;
	}
	mem_end = b;
	
	return b;
}	


struct bloc_s *more_mem(int size){
	
	struct bloc_s *bloc;
	struct bloc_s *bloc2;
	
	// On recupere l'adresse memoire de la fin
	////printf("MoreMem : Position Break DEBUT : %p \n",sbrk(0));
	
	bloc2 = sbrk(0);
	
	// On decale la fin
	if( sbrk((size)) == negVoid){
		return 0;
	}
	
	mem_end = sbrk(0);
	
	// On recupere l'adresse memoire de la fin
	////printf("MoreMem : Position Break Fin : %p \n",sbrk(0));
	
	// Premiere entree
	if(mem_start == 0){
		mem_start = mem_end - size;
		////printf("MoreMem : Position du Mem Staret : %p \n",mem_start);
		bloc = mem_start;
		bloc->prev = 0;
		
	} else {
		// Il y a deja un bloc existant
		bloc = find_last();
		bloc->next =bloc2;
		bloc->next->prev = bloc;
		bloc = bloc->next;
	}
	
	bloc->size = size - sizeof(struct bloc_s);
	bloc->free = 1;
	bloc->next = 0;
	return bloc;
}

/**
 * @todo Controler le size en parametre parce que ca peut etre plus grand
 */

struct bloc_s *bloc_split(struct bloc_s *bloc, int size){
	
	////printf("BlocSplit:  %d, %d\n", bloc->size, size);
	int size_init;	

	struct bloc_s *next;
	next = bloc->next;

	size_init = bloc->size;
	bloc->size = size;
	bloc->next = bloc + size + sizeof(struct bloc_s);//A controler
	bloc->next->next = next;
	bloc->next->free = 1;
	bloc->next->prev = bloc;
	bloc->next->size = size_init - sizeof(struct bloc_s) -size;
	
	if(next != 0){
		next->prev = bloc->next;
		//Faut controler si le bloc d'apres est free. SI il free on va le concatener avec notre dernier bloc.
		if(next->next != 0 && next->next->free){
			next->size = next->size + sizeof(struct bloc_s) + next->next->size;
			if(next->next->next != 0){
				next->next->next->prev = next;
			}
			next->next = next->next->next;
		}
	}
	
	return bloc->next;
}

void* myMalloc(int size){
	
	int done;
	int cp1;
	struct bloc_s *bloc;
	struct bloc_s *lbloc;

	done = 0;	
	lbloc = 0;
	
	if(mem_start==0){
		if( (size + sizeof(struct bloc_s)) < DEFAULTSIZE){
			if(more_mem(DEFAULTSIZE) == negVoid){
				return 0;
			}
		}else{
			if(more_mem(size + sizeof(struct bloc_s)) == negVoid){
				return 0;
			}
		}
	}
	
	bloc = mem_start;	
	while(bloc != 0){
		if(bloc->free == 1){
			cp1 = (bloc->size - sizeof(struct bloc_s) - MINSIZE);
			//On controle la taille du bloc pour vois si on peut l'utilise
			if( cp1 >= size || ( (bloc->size - sizeof(struct bloc_s)>=size) && size < MINSIZE) ){
				//Une fois splitter si la taille du second bloc reste assez grand on SPlit
				lbloc = bloc_split(bloc, size);
				bloc->free = 0;
				return bloc + 1;
				
			}else if((bloc->size - sizeof(struct bloc_s)) >= size){
				//Sinon on ne split pas on retourne un bloc trop grand. 
				bloc->free = 0;
				return bloc + 1;
			}
			
		}
		lbloc = bloc;
		bloc = bloc->next;
	}
	
	//Je controle si la taille par default est assze grande
	if(size > (DEFAULTSIZE- sizeof(struct bloc_s))){
		//Si elle n'est pas assez grande on cree un nouveau bloc.

		//On controle si le dernier bloc est vide. 
		if(lbloc != 0 && lbloc->free == 1){

			//Si oui le nouveau block sera compris dans le block d'avant. 
			bloc = more_mem(size - lbloc->size);

			if(bloc == negVoid){

				return 0;
			}
			
			lbloc->size = lbloc->size + bloc->size + sizeof(struct bloc_s);
			lbloc->next = 0;
			lbloc->free = 0;
	
			return lbloc + 1;
		}else{
			//Si le block d'avant est plein on cree un nouveau block de la bonne taille et le retourne
			bloc = more_mem(size + sizeof(struct bloc_s));

			if(bloc == negVoid){
				return 0;
			}
			bloc->free = 0;
			return bloc + 1;
		}
	}else{
		
		//On Alloue de la taille par default et en Split. 
		bloc = more_mem	(DEFAULTSIZE);
		if(bloc == negVoid){
			return 0;
		}
		
		bloc_split(bloc, size);
		bloc->free = 0;
		return bloc + 1;
	}
}

void myFree(void *ptr){
	
	struct bloc_s *bloc;

	struct bloc_s *abloc;
	
	bloc = ptr - sizeof(struct bloc_s); 
	abloc = mem_start;
	bloc->free = 1;
	
	//Si le Bloc d'apres est vide on va concatener les 2 bloc
	if(bloc->next != 0 && bloc->next->free == 1){
		bloc->size = bloc->size + bloc->next->size + sizeof(struct bloc_s);
		bloc->next = bloc->next->next;
		if(bloc->next != 0){
			bloc->next->prev = bloc;
		}
	}
	
	abloc = bloc->prev;
	//Si on a trouve le bloc d'avant on va concatener.
	if(abloc != 0 && abloc->free == 1){		
		abloc->size = abloc->size + bloc->size + sizeof(struct bloc_s);
		abloc->next = bloc->next; 
		if(abloc->next !=0){
			bloc->next->prev = abloc;
		}
	}
}

void *myReAlloc(void *ptr, int new_size){
	
	//Le nouveau bloc
	void* nvmem;
	
	int cpt;
	
	char *aoct;
	char *nvoct;

	//L'ancien bloc
	struct bloc_s *abloc;
	
	abloc = ptr - sizeof(struct bloc_s);

	if(abloc->size < new_size){

		//On allue un nouveau bloc
		nvmem = myMalloc(new_size);
        aoct = ptr;
        nvoct = nvmem;
        cpt = abloc->size;
        /* Copie */
        while(cpt-- > 0){
            *nvoct++ = *aoct++;
        }
        
        return nvmem;
		
	}else{
		return ptr;
	}
}

void initMyMalloc(){
	DEFAULTSIZE = 4096*128;
	MINSIZE = 4;
	mem_start = 0;
	mem_end = 0;
	negVoid = 0;
	negVoid--;
}
/********************************
	Les Fonction De Test
********************************/

void test_myReAlloc(){
	int i;
	int* tab;	

	//printf("\n ### Test du myReAlloc ###\n");
	//printf(" ! Devrait afficher des valeurs de 1 a 20 :\n");
	
	tab = myMalloc(10*sizeof(int));
	//printf("  |ETAPE 1 : myMalloc(10) DONE \n");	

	for(i=0; i<10; i++){
		*(tab+i) = i+1;
	}
	//printf("  |ETAPE 2, Affectation valeur 0 a 9 DONE\n");

	tab = myReAlloc(tab,20*sizeof(int));
	//printf("  |ETAPE 3, myReAlloc(20) DONE\n");	

	for(i=0; i<10; i++){
		//printf("\t \t v %d\n", *(tab+i));
	}
	//printf("  |ETAPE 4, Valeur Copie Affiche DONE\n");
	
	for(i=10; i<20; i++){
		*(tab+i) = i+1;
	}
	//printf("  |ETAPE 5, Affectation valeur 10 a 19 DONE\n");
	
	for(i=10; i<20; i++){
		//printf("\t \t v %d\n", *(tab+i));
	}
	//printf("  |ETAPE 6, Nouvelles valeures DONE\n");
	
	//printf(" ### Fin Test du myReAlloc ###\n");
}

void test_myMallocSimple(){
	int* tab;
	int i;

	//printf("\n ### Test du myMalloc ###\n");
	//printf(" ! Devrait afficher des valeurs de 1 a 20 :\n");

	tab = myMalloc(20*sizeof(int));

	//printf("  |ETAPE 1 : myMalloc(20) DONE \n");		

	for(i=0; i<20; i++){
		*(tab+i) = i;
	}
	//printf("  |ETAPE 2 : Affectation DONE\n");	

	for(i=0; i<20; i++){
		//printf("\t \t v %d\n", *(tab+i));
	}
	//printf("  |ETAPE 3 : Lecture DONE\n");
	
	myFree(tab);
	//printf("  |ETAPE 4 : free DONE\n");

	//printf(" ### Fin Test du myMalloc ###\n");		
}

int main(int argc, char ** argv){
	
	int t;
	t = 5;
	//printf(" Test %d\n", t);
	
	test_myMallocSimple();
	//test_myReAlloc();
	
}
